<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Fiber - 第二阶段：commit阶段</title>
    <!-- 加载React库 -->
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    <!-- 加载Babel编译器 -->
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/babel">
      const jsx = (
        <div id="a1">
          <div id="b1">
            <div id="c1"></div>
            <div id="c2"></div>
          </div>
          <div id="b2"></div>
        </div>
      );

      // 获取挂载的根节点
      const container = document.getElementById("root");

      // 1. 构建根Fiber: stateNode => fiber对象所对应的DOM对象
      const workInProgressRoot = {
        stateNode: container,
        props: { id: 'root', children: [jsx] },
      };

      // 2. 开始第一阶段：render阶段 => 通过循环（模拟递归）来构建子元素的fiber对象
      let nextUnitOfWork = workInProgressRoot; // 存储接下来执行的任务
      function workLoop(deadline) {
        // 循环条件：①是否在浏览器空闲时间 ②是否还存在需要执行的任务
        while (deadline.timeRemaining() > 0 && nextUnitOfWork) {
          nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
        }
        if (!nextUnitOfWork) {
          // 任务均执行完成，无接下来的任务 => 进入第二阶段 commit
          commitRoot();
        }
      }

      // 3. fiber的构建需要在浏览器闲时执行
      requestIdleCallback(workLoop);

      // 4. 处理单个任务
      function performUnitOfWork(workInProgressFiber) {
        // 构建单个fiber: ①创建DOM对象并存储在stateNode属性中,设置DOM的props ②构建当前fiber的子集fiber
        beginWork(workInProgressFiber);

        // 存在子级fiber，返回子级fiber，构建子级fiber
        if (workInProgressFiber.child) return workInProgressFiber.child;

        // 构建子集fiber
        while (workInProgressFiber) {
          // 构建链表
          completeUnitOfWork(workInProgressFiber);

          // 如果有兄弟节点，构建兄弟节点的fiber
          if (workInProgressFiber.sibling) {
            return workInProgressFiber.sibling;
          }

          // 更新父级
          workInProgressFiber = workInProgressFiber.return;
        }
      }

      // 5. 构建单个fiber任务
      function beginWork(workInProgressFiber) {
        // ①创建DOM对象并存储在stateNode属性中,设置DOM的props,其中RootFiber不需要此构建
        if (!workInProgressFiber.stateNode) {
          workInProgressFiber.stateNode = document.createElement(
            workInProgressFiber.type
          );
          for (let attr in workInProgressFiber.props) {
            if (attr !== "children") {
              workInProgressFiber.stateNode[attr] =
                workInProgressFiber.props[attr];
            }
          }
        }
        // ②构建当前fiber的子集fiber,
        if (Array.isArray(workInProgressFiber.props.children)) {
          // 存储本次循环中的上一个fiber
          let previousFiber = null;
          workInProgressFiber.props.children.forEach((child, index) => {
            // fiber的链表结构：父级节点是谁，子级节点是谁，以及下一个兄弟节点是谁（这样才能知道下一个fiber任务是谁）
            let childFiber = {
              type: child.type,
              props: child.props,
              effectTag: "PLACEMENT", // 表示fiber当前执行DOM操作
              return: workInProgressFiber, // 父层fiber
            };
            if (index === 0) {
              // children的第一项作为workInProgressFiber的子级节点
              workInProgressFiber.child = childFiber;
            } else {
              // 其他项作为上一个fiber的兄弟节点
              previousFiber.sibling = childFiber;
            }
            // 记录本次循环中的上一个fiber
            previousFiber = childFiber;
          });
        }

        // console.log('workInProgressFiber:')
        // console.log(workInProgressFiber)
      }

      // 6. 构建链表
      function completeUnitOfWork(workInProgressFiber) {
        /*
        ** firstEffect：指向当前链表中的第一个Fiber节点
        ** lastEffect： 指向当前链表中最后一个Fiber节点，即上一个操作的Fiber节点；记录在父层Fiber里
        ** nextEffect：指向当前链表中的下一个Fiber节点

        ** 需要处理的内容：
        ** 1. currentFiber.firstEffect = firstFiber
        ** 2. parentFiber.lastEffect = currentFiber
        ** 3. currentFiber.nextEffect = nextFiber => lastFiber.nextEffect = currentFiber 链表里无法获取下一个fiber，只能设置上一个fiber的nextEffect属性为当前fiber
        */
        console.log("--------------");
        console.log(workInProgressFiber.props.id);
        // 获取当前 Fiber 的父级
        const returnFiber = workInProgressFiber.return;

        if (returnFiber) {
          if (workInProgressFiber.effectTag) {
            if (!returnFiber.lastEffect) {
              // 4. 父层无记录上一个节点，获取
              returnFiber.lastEffect = workInProgressFiber.lastEffect;
              console.log(
                `4. ${returnFiber.props.id}.lastEffect = ${
                  workInProgressFiber.lastEffect
                    ? workInProgressFiber.lastEffect.props.id
                    : "null"
                }`
              );
            }
            if (!returnFiber.firstEffect) {
              // 5. 设置firstEffect
              returnFiber.firstEffect = workInProgressFiber.firstEffect;
              console.log(
                `5. ${returnFiber.props.id}.firstEffect = ${
                  workInProgressFiber.firstEffect
                    ? workInProgressFiber.firstEffect.props.id
                    : "null"
                }`
              );
            }
            if (returnFiber.lastEffect) {
              // 2. 说明链表里，当前父层节点有记录上一个节点 => 设置上一个节点的nextEffect属性为当前节点
              returnFiber.lastEffect.nextEffect = workInProgressFiber;
              console.log(
                `2. ${returnFiber.lastEffect.props.id}.nextEffect = ${workInProgressFiber.props.id}`
              );
            } else {
              // 3. 没有记录上一个节点，则设置当前节点为父层的第一个节点
              returnFiber.firstEffect = workInProgressFiber;
              console.log(
                `3. ${returnFiber.props.id}.firstEffect = ${workInProgressFiber.props.id}`
              );
            }
            // 1. 更新链表尾部：将链表的最新项存储在父层的lastEffect
            returnFiber.lastEffect = workInProgressFiber;
            console.log(
              `1. ${returnFiber.props.id}.lastEffect = ${workInProgressFiber.props.id}`
            );
          }
        }
      }

      // 7. 开始第二阶段：commit
      function commitRoot() {
        // console.log('Fiber Tree')
        // console.log(workInProgressRoot)
        let currentFiber = workInProgressRoot.firstEffect;
        while (currentFiber) {
          // 只考虑追加
          currentFiber.return.stateNode.appendChild(currentFiber.stateNode);
          currentFiber = currentFiber.nextEffect;
        }
      }
    </script>
  </body>
</html>
